React Router v6 
快速复习 
- BrowerRouter
 - NavLink vs Link (可以传递参数)
 - Routes && Route
 - useNavigate 强制路由跳转
 - Link && Navigate && useNavigate 通过 state 传递状态,通过 useLocation 接收
 - 通过 useParams 接收 URL 参数
 
一、概述 
如何安装 
pnpm add react-router-dom@6
概念 
- react-router:为 React 应用提供了路由的核心功能;
 - react-router-dom:基于 react-router,加入了在浏览器运行环境下的一些功能。
 
二、基本使用 
BrowserRouter 
要想在 React 应用中使用 React Router,就需要在 React 项目的根文件(index.tsx)中导入 Router 组件
import { StrictMode } from "react";
import * as ReactDOMClient from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
const rootElement = document.getElementById("root");
const root = ReactDOMClient.createRoot(rootElement);
root.render(
  《StrictMode》
    《BrowserRouter》
      《App /》
    《/BrowserRouter》
  《/StrictMode》
)BrowserRouter 是最常用的路由方式,即浏览器路由。官方文档也建议将 BrowserRouter 组件用于 Web 应用程序。除了这种方式,React Router 还支持其他几种路由方式:
- HashRouter:在路径前加入#成为一个哈希值,Hash 模式的好处是不会因为刷新页面而找不到对应路径;
 - MemoryRouter:不存储 history,路由过程保存在内存中,适用于 React Native 这种非浏览器环境;
 - NativeRouter:配合 React Native 使用,多用于移动端;
 - StaticRouter:主要用于服务端渲染时。
 
NavLink 
NavLink 组件,它是一个导航链接组件,类似于 HTML 中的《a》标签。NavLink 组件使用 to 来指定需要跳转的链接,在TSX中代码如下:
《NavLink to="product"》产品《/NavLink》
NavLink 是存在 active 状态的,所以可以为active 状态和非active 状态的导航链接添加样式:
import { NavLink } from "react-router-dom";
import "./styles.css";
export default function App() {
  return (
    《div className="App"》
      《header》
        《h1》Hello World《/h1》
      《/header》
      《nav》
        《NavLink
    			to=""
          className={({ isActive }) =》 isActive ? "nav-active" : void 0}
        》
          首页
        《/NavLink》
        《NavLink to="product"》产品《/NavLink》
        《NavLink to="about"》关于《/NavLink》
      《/nav》
    《/div》
  );
}```
### Link🌟
Link 组件与 NavLink 组件非常相似,唯一的区别就是 NavLink 存在 active 状态,而 Link 没有。
如果需要对 Link 进行更多控制,也可以传递给 to 一个对象,在这个对象中,可以通过 search 属性来添加查询字符串或通过 hash 属性来传递 hash值,例如:
```javascript
import { Link } from "react-router-dom";
import "./styles.css";
export default function Settings() {
  return (
    《div className="settings"》
      《header》
        《h1》Hello World《/h1》
        《Link
          to={{
            pathname: "/settings",
            search: "?sort=date",
            hash: "#hash"
          }}
        》
          设置
        《/Link》
      《/header》
    《/div》
  );
}此时路由就变成了 /settings?sort=date#hash
Routes 
在 Routes 组件中使用 Route 组件来定义所有路由。该组件接受两个 props:
- path:页面 URL 应导航到的路径,类似于 NavLink 组件的 to;
 - element:页面导航到该路由时加载的元素。
 
import { NavLink, Routes, Route } from "react-router-dom";
import Product from "./Product";
import About from "./About";
import Home from "./Home";
import Error from "./Error";
import "./styles.css";
export default function App() {
  return (
    《div className="App"》
      《header》
        《h1》Hello World《/h1》
      《/header》
      《nav》
        《NavLink to=""》首页《/NavLink》
        《NavLink to="product"》产品《/NavLink》
        《NavLink to="about"》关于《/NavLink》
      《/nav》
      《Routes》
        《Route path="/" element={《Home /》} /》
        《Route path="/product" element={《Product /》} /》
        《Route path="/about" element={《About /》} /》
        《Route path="*" element={《Error /》} /》
      《/Routes》
    《/div》
  );
}路由顺序 
在 React Router v6 以前,我们必须按照一定的顺序来定义路由,以获得准确的渲染。在 v6 及之后的版本中,路由定义的顺序无关紧要。
v5中,/product/new 会匹配到 Product 组件
《Switch》
  《Route path="/product/:id" component={Product} /》
  《Route path="/product/new" component={NewProduct} /》
《/Switch》v6 中,将 《Switch》 组件替换为了 《Routes》 组件。/products/new将匹配这两个路由,但只会渲染NewProduct组件,因为它是更具体的匹配:
《Routes》
  《Route path="/product/:id" element={《Product /》} /》
  《Route path="/product/new" element={《NewProduct /》} /》
《/Routes》三、编程式导航 
React Router 提供了两种不同的编程式导航方式:
- 声明式导航组件:《Navigate》 组件
 - 命令式导航方法:useNavigate Hook
 
可以使用这两种编程的方式来跳转到指定的路由,也可以实现路由的重定向,比如在浏览器的地址栏输入一个 URL 并进行跳转时,如果应用中没有定义该路由,就跳转到应用的首页。
Navigate 
和 Navlink 类似,Navigate 组件中通过 to props 来指定要跳转的路径。
import { NavLink, Routes, Route, Navigate } from "react-router-dom";
import Product from "./Product";
import About from "./About";
import Home from "./Home";
import "./styles.css";
export default function App() {
  return (
    《div className="App"》
      《header》
        《h1》Hello World《/h1》
      《/header》
      《nav》
        《NavLink to=""》首页《/NavLink》
        《NavLink to="product"》产品《/NavLink》
        《NavLink to="about"》关于《/NavLink》
      《/nav》
      《Routes》
        《Route path="/" element={《Home /》} /》
        《Route path="product" element={《Product /》} /》
        《Route path="about" element={《About /》} /》
        《Route path="*" element={《Navigate to="/" /》} /》 // 看这里
      《/Routes》
    《/div》
  );
}```
这样,当在浏览器地址栏输入一个未定义的路由时,就会跳转到首页。
### useNavigate🌟
1. 导入 Hook
2. 创建实例
3. 在函数中进行使用import { useNavigate } from 'react-router-dom
function Register () { const navigate = useNavigate()
return ( 《div》 《Form afterSubmit={() =》 navigate('/')} /》 // 提交完表单之后,跳转到主页 《/div》 ) }
## 四、通过路由传递状态
在 react-router-dom 中可以通过以下三种方式来传递状态,它们的传递方式有区分:
- 使用 Link 组件
- 使用 Navigate 组件
- 使用 useNavigate 钩子
但它们在页面中的获取方式相同,都是通过 **useLocation** 钩子来**获取状态**
### Link && useLocation
Link 组件通过 state props 来将数据从产品页面传递到主页
`《Link to="/" state={"From Product"}》返回《/Link》`
使用一个名为 **useLocation** 的钩子来获取数据import { useLocation } from "react-router-dom"; // 1.导入 Hook import "./styles.css";
export default function Settings() { let location = useLocation(); // 2.定义实例 return ( 《div className="App"》 《header》首页《/header》 《p》{location.state}《/p》 // 3.页面中使用 《/div》 ); }
### Navigate
Navigate 组件也是通过state属性传递状态, 在页面中也是通过useLocation获取状态
`《Route path="/about" element={《Navigate to="/" state={"From About"} /》} /》`
### useNavigate
使用useNavigate钩子创建的navigate函数有两个参数
第一个是 跳转路由, 第二个是包含状态的对象
```javascript
import { useNavigate } from 'react-router-dom
function Register () {
  const navigate = useNavigate()
  return (
    《div》
      《Form afterSubmit={() =》 navigate('/', { state: "From the About Page"})} /》 // 包含state状态的对象
    《/div》
  )
}在首页中仍然是使用 useLocation 钩子来获取状态值
五、动态路由 
声明一个带有 keyword 占位符的路由即可。对于上面的例子,只需要将 Route 组件的 path props 声明为这样
《Route path="/wiki/:keyword" element={《Wiki /》} /》
如何在组件中访问 URL 中的动态部分呢 -》 使用 useParams 钩子
import React from 'react'
import {useParams} from 'react-router';
function Wiki() {
  const { keyword } = useParams()
  
  return (
    《div》{ keyword }《/div》
  )
}六、嵌套路由 
使用 Outlet 配置嵌套路由
固定 navbar, 更换 main 
const Layout = () =》 {
  return (
    《》
      《Navbar /》
      《Outlet /》
      《Footer /》
    《/》
  );
};
const router = createBrowserRouter([
  {
    path: "/",
    element: 《Layout /》,
    children: [
      {
        path: "/",
        element: 《Home /》,
      },
      {
        path: "/post/:id",
        element: 《Single /》,
      },
      {
        path: "/write",
        element: 《Write /》,
      },
    ],
  },
  {
    path: "/register",
    element: 《Register /》,
  },
  {
    path: "/login",
    element: 《Login /》,
  },
]);
```
### 展示 messages
```javascript
function App() {  
  const router = createBrowserRouter([
    {
      path: "/",
      element: 《Home /》,
    },
    {
      path: "/messages",
      element: 《Messages /》,
      children: [
      	{
    			path: ':id',
      		element: 《MessagesDetails /》
    		}
      ]
    },
  ]);
  return (
    《div className="App"》
      《RouterProvider router={router} /》
    《/div》
  );
}
```
这里
```javascript
import { Outlet } from "react-router-dom";
function Messages() {
  return (
    《div》
      《Conversations /》
      《Outlet /》 // 路由为messgaes/id时,会在此渲染 MessagesDetails 组件
    《/div》
  );
}
```
## 七、查询参数
React Router 提供了一个自定义的 useSearchParams Hook,它是基于 URLSearchParams 进行的封装。useSearchParams 返回一个数组,该数组第一个元素是 URLSearchParams 的实例,第二个元素是更新查询参数的一个方法。
`import { useSearchParams } from 'react-router-dom'
const Results = () =》 {
  const [searchParams, setSearchParams] = useSearchParams();
  const q = searchParams.get('q')
  const src = searchParams.get('src')
  const f = searchParams.get('f')
  return (
    // ...
  )
}`
如果需要更新查询字符串,可以使用 setSearchParams,向它传递一个对象,该对象的key/value 对将作为 &key=value 添加到 url:
`const Results = () =》 {
  const [searchParams, setSearchParams] = useSearchParams();
  const q = searchParams.get('q')
  const src = searchParams.get('src')
  const f = searchParams.get('f')
  const updateOrder = (sort) =》 {
    setSearchParams({ sort })
  }
  return (
    ...
  )
}`
## 八、路由配置
在 v5 中,通常使用 createBrowserRouter 来创建 router 示例,再把 router 实例作为参数传递给 《RouterProvider router={router} /》
在 v6 中,可以使用 useRoutes 钩子,直接将路由渲染到页面中
`export default function App() {
  return (
    《div》
      《Navbar /》
      《Routes》
        《Route path="/" element={《Home /》} /》
        《Route path="/invoices" element={《Invoices /》}》
          《Route path=":id" element={《Invoice /》} /》
          《Route path="pending" element={《Pending /》} /》
          《Route path="complete" element={《Complete /》} /》
        《/Route》
      《/Routes》
    《/div》
  );
}`
`import { useRoutes } from "react-router-dom";
const routes = useRoutes([
  { path: "/", element: 《Home /》 },
  {
    path: "/invoices",
    element: 《Invoices /》,
    children: [
      { path: ":id", element: 《Invoice /》 },
      { path: "/pending", element: 《Pending /》 },
      { path: "/complete", element: 《Complete /》 },
    ],
  },
]);
export default function App() {
  return (
    《div》
      《Navbar /》
      {routes}
    《/div》
  );
}`
@luminous/docs